#include "PortManager.h"

#include <memory.h>
#include <sstream>
#include "Config.h"
#include "Debug.h"

using namespace std;

CPortManager::CPortManager(const CConfig &conf):
m_conf(conf)
{
  memset(&m_SipPorts, 0, sizeof(m_SipPorts));
  memset(&m_RtpPorts, 0, sizeof(m_RtpPorts));
  pthread_mutex_init(&m_mutex, NULL);
}

CPortManager::~CPortManager()
{
  if(m_SipPorts.plStates) {
    delete [] m_SipPorts.plStates;
  }
  if(m_RtpPorts.plStates) {
    delete [] m_RtpPorts.plStates;
  }
  pthread_mutex_destroy(&m_mutex);
}

bool CPortManager::Init()
{
  PortRange *pRange = &m_SipPorts;
  ConfigEntry entry = CE_LOWSIPPORT;
  istringstream is;
  for(int i = 0; i < 2; i++) {
    is.str(GetValue(entry));
    is >> pRange->usLow;
    if(is.fail())
      return false;
    entry = (ConfigEntry)(entry + 1);
    is.clear();
    is.str(GetValue(entry));
    is >> pRange->usSize;
    if(is.fail())
      return false;
    entry = (ConfigEntry)(entry + 1);
    is.clear();

    pRange->usHigh = pRange->usLow + pRange->usSize - 1;
    pRange->usUsed = 0;
    pRange->usNext = pRange->usLow;
    pRange->plStates = new long[pRange->usSize];
    memset(pRange->plStates, 0, pRange->usSize * sizeof(long));
    pRange = &m_RtpPorts;
  }
  m_RtpPorts.usHigh += m_RtpPorts.usSize;
  return true;
}

bool CPortManager::GetPort(bool bRtp, unsigned short &usPort)
{
  int _iIndex, _iIncrement = 1;
  bool _ret = false;
  PortRange *pRange = &m_SipPorts;
  if(bRtp) {
    _iIncrement++;
    pRange = &m_RtpPorts;
  }
  
  pthread_mutex_lock(&m_mutex);
  if(pRange->usUsed < pRange->usSize) {
    while(!_ret) {
      _iIndex = (pRange->usNext - pRange->usLow) / _iIncrement;
      if(!pRange->plStates[_iIndex]) {
        usPort = pRange->usNext;
        pRange->usUsed++;
        pRange->plStates[_iIndex] = 1;
        _ret = true;
      }
      pRange->usNext = (pRange->usNext + _iIncrement) % (pRange->usHigh + 1);
    }
  }
  pthread_mutex_unlock(&m_mutex);
  return _ret;
}

bool CPortManager::ReleasePort(bool bRtp, unsigned short usPort)
{
  int _iIncrement = 1;
  PortRange *pRange = &m_SipPorts;
  if(bRtp) {
    _iIncrement++;
    pRange = &m_RtpPorts;
  } 
   
  if(usPort < pRange->usLow || usPort > pRange->usHigh)
    return false;
  pthread_mutex_lock(&m_mutex);
  pRange->plStates[(pRange->usNext - pRange->usLow) / _iIncrement] = 0;
  pRange->usUsed--;
  pthread_mutex_unlock(&m_mutex);
  return true;
}
